#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstdio>
#include <cstdlib>

#include "client_comm.h"
#include "client_serialize.h"

#define eprintf(x...) do { printf(x); ::exit(1); } while (false)

// message buffer, should be bigger than 64K.
const int MAX_ENCAP_MESSAGE_LEN = 70000; 

/**
 * rwrite(fildes, buf, nbyte)
 * A debug-enabled equivalent to write(fildes, buf, nbyte).
 */
ssize_t rwrite(int fildes, const void *buf, size_t nbyte)
{
#ifdef DEBUG
	// se em modo de DEBUG, imprime os codigos hex dos bytes enviados.
	const char *p = (const char*)buf;
	for (size_t i = 0; i < nbyte; ++i)
		fprintf(stderr, "0x%hhx ", *p++);
#endif
	return write(fildes, buf, nbyte);
}

/**
 * rread(fildes, buf, nbyte)
 * A debug-enabled equivalent to read(fildes, buf, nbyte).
 */
ssize_t rread(int fildes, void *buf, size_t nbyte)
{
	ssize_t ret = read(fildes, buf, nbyte);
#ifdef DEBUG
	// se em modo DEBUG, imprime os codigos hex dos bytes recebidos.
	const char *p = (const char*)buf;
	for (size_t i = 0; i < nbyte; ++i)
		fprintf(stderr, "0x%hhx ", *p++);
#endif
	return ret;
}

/**
 * sendall(fd, len, buf)
 * Makes enough send calls in order to send all required bytes to target.
 */
int sendall(int fd, int len, char *buf)
{
	int sent = 0;
	while (sent < len)
	{
		int cursent = rwrite(fd, buf + sent, len - sent);
		if (cursent <= 0)
			return -1;
		sent += cursent;
	}
	return 0;
}

/**
 * recvall(fd, len, buf)
 * Makes enough read calls in order to read all required bytes from source.
 */
int recvall(int fd, int len, char *buf)
{
	int read = 0;
	while (read < len)
	{
		int curread = rread(fd, buf + read, len - read);
		if (curread <= 0)
			return -1;
		read += curread;
	}
	return 0;
}

/**
 * sendMessageLC(fd, message)
 * Encapsulates and sends a (serialized) MessageLC structure through
 * desired file descriptor.
 */
int sendMessageLC(int fd, MessageLC *message)
{
	char bufserial[MAX_ENCAP_MESSAGE_LEN], bufencap[MAX_ENCAP_MESSAGE_LEN];
	uint16_t len = serializeLC(bufserial, message);
	int encaplen = encapsulate(bufencap, len, bufserial);
	return sendall(fd, encaplen, bufencap);
}

/**
 * sendMessageLS(fd, message)
 * Encapsulates and sends a (serialized) MessageLS structure through
 * desired file descriptor.
 */
int sendMessageLS(int fd, MessageLS *message)
{
	char bufserial[MAX_ENCAP_MESSAGE_LEN], bufencap[MAX_ENCAP_MESSAGE_LEN];
	uint16_t len = serializeLS(bufserial, message);
	int encaplen = encapsulate(bufencap, len, bufserial);
	return sendall(fd, encaplen, bufencap);
}

/**
 * recvMessage(fd, buf)
 * Receives and stores a raw message into the desired buffer.
 */
int recvMessage(int fd, char *buf)
{
	char lenbuf[2];
	if (recvall(fd, 2, lenbuf) == -1)
		return -1;
	uint16_t msglen;
	unpack(lenbuf, "h", &msglen);	
	if (recvall(fd, msglen, buf) == -1)
		return -1;
	return 0;
}

/**
 * recvMessageCL(fd)
 * Receives and deserializes a MessageCL structure.
 */
MessageCL *recvMessageCL(int fd)
{
	char msgbuf[MAX_ENCAP_MESSAGE_LEN];
	if (recvMessage(fd, msgbuf) == -1)
		return NULL;
	return deserializeCL(msgbuf);
}

/**
 * recvMessageSL(fd)
 * Receives and deserializes a MessageSL structure.
 */
MessageSL *recvMessageSL(int fd)
{
	char msgbuf[MAX_ENCAP_MESSAGE_LEN];
	if (recvMessage(fd, msgbuf) == -1)
		return NULL;
	return deserializeSL(msgbuf);
}

